/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.dex.code;

import java.util.HashMap;

import com.android.dx.rop.code.Insn;
import com.android.dx.rop.code.RegOps;
import com.android.dx.rop.code.RegisterSpec;
import com.android.dx.rop.code.Rop;
import com.android.dx.rop.code.Rops;
import com.android.dx.rop.code.ThrowingCstInsn;
import com.android.dx.rop.cst.Constant;
import com.android.dx.rop.cst.CstFieldRef;
import com.android.dx.rop.cst.CstString;
import com.android.dx.rop.cst.CstType;
import com.android.dx.rop.type.Type;

/**
 * Translator from rop-level {@link Insn} instances to corresponding {@link Dop}
 * instances.
 */
public final class RopToDop {

	/** {@code non-null;} map from all the common rops to dalvik opcodes */
	private static final HashMap<Rop, Dop> MAP;

	/**
	 * This class is uninstantiable.
	 */
	private RopToDop() {
		// This space intentionally left blank.
	}

	/*
	 * The following comment lists each opcode that should be considered the
	 * "head" of an opcode chain, in terms of the process of fitting an
	 * instruction's arguments to an actual opcode. This list is automatically
	 * generated and may be of use in double-checking the manually-generated
	 * static initialization code for this class.
	 * 
	 * TODO: Make opcode-gen produce useful code in this case instead of just a
	 * comment.
	 */

	// BEGIN(first-opcodes); GENERATED AUTOMATICALLY BY opcode-gen
	// Opcodes.NOP
	// Opcodes.MOVE
	// Opcodes.MOVE_WIDE
	// Opcodes.MOVE_OBJECT
	// Opcodes.MOVE_RESULT
	// Opcodes.MOVE_RESULT_WIDE
	// Opcodes.MOVE_RESULT_OBJECT
	// Opcodes.MOVE_EXCEPTION
	// Opcodes.RETURN_VOID
	// Opcodes.RETURN
	// Opcodes.RETURN_WIDE
	// Opcodes.RETURN_OBJECT
	// Opcodes.CONST_4
	// Opcodes.CONST_WIDE_16
	// Opcodes.CONST_STRING
	// Opcodes.CONST_CLASS
	// Opcodes.MONITOR_ENTER
	// Opcodes.MONITOR_EXIT
	// Opcodes.CHECK_CAST
	// Opcodes.INSTANCE_OF
	// Opcodes.ARRAY_LENGTH
	// Opcodes.NEW_INSTANCE
	// Opcodes.NEW_ARRAY
	// Opcodes.FILLED_NEW_ARRAY
	// Opcodes.FILL_ARRAY_DATA
	// Opcodes.THROW
	// Opcodes.GOTO
	// Opcodes.PACKED_SWITCH
	// Opcodes.SPARSE_SWITCH
	// Opcodes.CMPL_FLOAT
	// Opcodes.CMPG_FLOAT
	// Opcodes.CMPL_DOUBLE
	// Opcodes.CMPG_DOUBLE
	// Opcodes.CMP_LONG
	// Opcodes.IF_EQ
	// Opcodes.IF_NE
	// Opcodes.IF_LT
	// Opcodes.IF_GE
	// Opcodes.IF_GT
	// Opcodes.IF_LE
	// Opcodes.IF_EQZ
	// Opcodes.IF_NEZ
	// Opcodes.IF_LTZ
	// Opcodes.IF_GEZ
	// Opcodes.IF_GTZ
	// Opcodes.IF_LEZ
	// Opcodes.AGET
	// Opcodes.AGET_WIDE
	// Opcodes.AGET_OBJECT
	// Opcodes.AGET_BOOLEAN
	// Opcodes.AGET_BYTE
	// Opcodes.AGET_CHAR
	// Opcodes.AGET_SHORT
	// Opcodes.APUT
	// Opcodes.APUT_WIDE
	// Opcodes.APUT_OBJECT
	// Opcodes.APUT_BOOLEAN
	// Opcodes.APUT_BYTE
	// Opcodes.APUT_CHAR
	// Opcodes.APUT_SHORT
	// Opcodes.IGET
	// Opcodes.IGET_WIDE
	// Opcodes.IGET_OBJECT
	// Opcodes.IGET_BOOLEAN
	// Opcodes.IGET_BYTE
	// Opcodes.IGET_CHAR
	// Opcodes.IGET_SHORT
	// Opcodes.IPUT
	// Opcodes.IPUT_WIDE
	// Opcodes.IPUT_OBJECT
	// Opcodes.IPUT_BOOLEAN
	// Opcodes.IPUT_BYTE
	// Opcodes.IPUT_CHAR
	// Opcodes.IPUT_SHORT
	// Opcodes.SGET
	// Opcodes.SGET_WIDE
	// Opcodes.SGET_OBJECT
	// Opcodes.SGET_BOOLEAN
	// Opcodes.SGET_BYTE
	// Opcodes.SGET_CHAR
	// Opcodes.SGET_SHORT
	// Opcodes.SPUT
	// Opcodes.SPUT_WIDE
	// Opcodes.SPUT_OBJECT
	// Opcodes.SPUT_BOOLEAN
	// Opcodes.SPUT_BYTE
	// Opcodes.SPUT_CHAR
	// Opcodes.SPUT_SHORT
	// Opcodes.INVOKE_VIRTUAL
	// Opcodes.INVOKE_SUPER
	// Opcodes.INVOKE_DIRECT
	// Opcodes.INVOKE_STATIC
	// Opcodes.INVOKE_INTERFACE
	// Opcodes.NEG_INT
	// Opcodes.NOT_INT
	// Opcodes.NEG_LONG
	// Opcodes.NOT_LONG
	// Opcodes.NEG_FLOAT
	// Opcodes.NEG_DOUBLE
	// Opcodes.INT_TO_LONG
	// Opcodes.INT_TO_FLOAT
	// Opcodes.INT_TO_DOUBLE
	// Opcodes.LONG_TO_INT
	// Opcodes.LONG_TO_FLOAT
	// Opcodes.LONG_TO_DOUBLE
	// Opcodes.FLOAT_TO_INT
	// Opcodes.FLOAT_TO_LONG
	// Opcodes.FLOAT_TO_DOUBLE
	// Opcodes.DOUBLE_TO_INT
	// Opcodes.DOUBLE_TO_LONG
	// Opcodes.DOUBLE_TO_FLOAT
	// Opcodes.INT_TO_BYTE
	// Opcodes.INT_TO_CHAR
	// Opcodes.INT_TO_SHORT
	// Opcodes.ADD_INT_2ADDR
	// Opcodes.SUB_INT_2ADDR
	// Opcodes.MUL_INT_2ADDR
	// Opcodes.DIV_INT_2ADDR
	// Opcodes.REM_INT_2ADDR
	// Opcodes.AND_INT_2ADDR
	// Opcodes.OR_INT_2ADDR
	// Opcodes.XOR_INT_2ADDR
	// Opcodes.SHL_INT_2ADDR
	// Opcodes.SHR_INT_2ADDR
	// Opcodes.USHR_INT_2ADDR
	// Opcodes.ADD_LONG_2ADDR
	// Opcodes.SUB_LONG_2ADDR
	// Opcodes.MUL_LONG_2ADDR
	// Opcodes.DIV_LONG_2ADDR
	// Opcodes.REM_LONG_2ADDR
	// Opcodes.AND_LONG_2ADDR
	// Opcodes.OR_LONG_2ADDR
	// Opcodes.XOR_LONG_2ADDR
	// Opcodes.SHL_LONG_2ADDR
	// Opcodes.SHR_LONG_2ADDR
	// Opcodes.USHR_LONG_2ADDR
	// Opcodes.ADD_FLOAT_2ADDR
	// Opcodes.SUB_FLOAT_2ADDR
	// Opcodes.MUL_FLOAT_2ADDR
	// Opcodes.DIV_FLOAT_2ADDR
	// Opcodes.REM_FLOAT_2ADDR
	// Opcodes.ADD_DOUBLE_2ADDR
	// Opcodes.SUB_DOUBLE_2ADDR
	// Opcodes.MUL_DOUBLE_2ADDR
	// Opcodes.DIV_DOUBLE_2ADDR
	// Opcodes.REM_DOUBLE_2ADDR
	// Opcodes.ADD_INT_LIT8
	// Opcodes.RSUB_INT_LIT8
	// Opcodes.MUL_INT_LIT8
	// Opcodes.DIV_INT_LIT8
	// Opcodes.REM_INT_LIT8
	// Opcodes.AND_INT_LIT8
	// Opcodes.OR_INT_LIT8
	// Opcodes.XOR_INT_LIT8
	// Opcodes.SHL_INT_LIT8
	// Opcodes.SHR_INT_LIT8
	// Opcodes.USHR_INT_LIT8
	// END(first-opcodes)

	static {
		/*
		 * Note: The choices made here are to pick the optimistically smallest
		 * Dalvik opcode, and leave it to later processing to pessimize. See the
		 * automatically-generated comment above for reference.
		 */
		MAP = new HashMap<Rop, Dop>(400);
		MAP.put(Rops.NOP, Dops.NOP);
		MAP.put(Rops.MOVE_INT, Dops.MOVE);
		MAP.put(Rops.MOVE_LONG, Dops.MOVE_WIDE);
		MAP.put(Rops.MOVE_FLOAT, Dops.MOVE);
		MAP.put(Rops.MOVE_DOUBLE, Dops.MOVE_WIDE);
		MAP.put(Rops.MOVE_OBJECT, Dops.MOVE_OBJECT);
		MAP.put(Rops.MOVE_PARAM_INT, Dops.MOVE);
		MAP.put(Rops.MOVE_PARAM_LONG, Dops.MOVE_WIDE);
		MAP.put(Rops.MOVE_PARAM_FLOAT, Dops.MOVE);
		MAP.put(Rops.MOVE_PARAM_DOUBLE, Dops.MOVE_WIDE);
		MAP.put(Rops.MOVE_PARAM_OBJECT, Dops.MOVE_OBJECT);

		/*
		 * Note: No entry for MOVE_EXCEPTION, since it varies by exception type.
		 * (That is, there is no unique instance to add to the map.)
		 */

		MAP.put(Rops.CONST_INT, Dops.CONST_4);
		MAP.put(Rops.CONST_LONG, Dops.CONST_WIDE_16);
		MAP.put(Rops.CONST_FLOAT, Dops.CONST_4);
		MAP.put(Rops.CONST_DOUBLE, Dops.CONST_WIDE_16);

		/*
		 * Note: No entry for CONST_OBJECT, since it needs to turn into either
		 * CONST_STRING or CONST_CLASS.
		 */

		/*
		 * TODO: I think the only case of this is for null, and const/4 should
		 * cover that.
		 */
		MAP.put(Rops.CONST_OBJECT_NOTHROW, Dops.CONST_4);

		MAP.put(Rops.GOTO, Dops.GOTO);
		MAP.put(Rops.IF_EQZ_INT, Dops.IF_EQZ);
		MAP.put(Rops.IF_NEZ_INT, Dops.IF_NEZ);
		MAP.put(Rops.IF_LTZ_INT, Dops.IF_LTZ);
		MAP.put(Rops.IF_GEZ_INT, Dops.IF_GEZ);
		MAP.put(Rops.IF_LEZ_INT, Dops.IF_LEZ);
		MAP.put(Rops.IF_GTZ_INT, Dops.IF_GTZ);
		MAP.put(Rops.IF_EQZ_OBJECT, Dops.IF_EQZ);
		MAP.put(Rops.IF_NEZ_OBJECT, Dops.IF_NEZ);
		MAP.put(Rops.IF_EQ_INT, Dops.IF_EQ);
		MAP.put(Rops.IF_NE_INT, Dops.IF_NE);
		MAP.put(Rops.IF_LT_INT, Dops.IF_LT);
		MAP.put(Rops.IF_GE_INT, Dops.IF_GE);
		MAP.put(Rops.IF_LE_INT, Dops.IF_LE);
		MAP.put(Rops.IF_GT_INT, Dops.IF_GT);
		MAP.put(Rops.IF_EQ_OBJECT, Dops.IF_EQ);
		MAP.put(Rops.IF_NE_OBJECT, Dops.IF_NE);
		MAP.put(Rops.SWITCH, Dops.SPARSE_SWITCH);
		MAP.put(Rops.ADD_INT, Dops.ADD_INT_2ADDR);
		MAP.put(Rops.ADD_LONG, Dops.ADD_LONG_2ADDR);
		MAP.put(Rops.ADD_FLOAT, Dops.ADD_FLOAT_2ADDR);
		MAP.put(Rops.ADD_DOUBLE, Dops.ADD_DOUBLE_2ADDR);
		MAP.put(Rops.SUB_INT, Dops.SUB_INT_2ADDR);
		MAP.put(Rops.SUB_LONG, Dops.SUB_LONG_2ADDR);
		MAP.put(Rops.SUB_FLOAT, Dops.SUB_FLOAT_2ADDR);
		MAP.put(Rops.SUB_DOUBLE, Dops.SUB_DOUBLE_2ADDR);
		MAP.put(Rops.MUL_INT, Dops.MUL_INT_2ADDR);
		MAP.put(Rops.MUL_LONG, Dops.MUL_LONG_2ADDR);
		MAP.put(Rops.MUL_FLOAT, Dops.MUL_FLOAT_2ADDR);
		MAP.put(Rops.MUL_DOUBLE, Dops.MUL_DOUBLE_2ADDR);
		MAP.put(Rops.DIV_INT, Dops.DIV_INT_2ADDR);
		MAP.put(Rops.DIV_LONG, Dops.DIV_LONG_2ADDR);
		MAP.put(Rops.DIV_FLOAT, Dops.DIV_FLOAT_2ADDR);
		MAP.put(Rops.DIV_DOUBLE, Dops.DIV_DOUBLE_2ADDR);
		MAP.put(Rops.REM_INT, Dops.REM_INT_2ADDR);
		MAP.put(Rops.REM_LONG, Dops.REM_LONG_2ADDR);
		MAP.put(Rops.REM_FLOAT, Dops.REM_FLOAT_2ADDR);
		MAP.put(Rops.REM_DOUBLE, Dops.REM_DOUBLE_2ADDR);
		MAP.put(Rops.NEG_INT, Dops.NEG_INT);
		MAP.put(Rops.NEG_LONG, Dops.NEG_LONG);
		MAP.put(Rops.NEG_FLOAT, Dops.NEG_FLOAT);
		MAP.put(Rops.NEG_DOUBLE, Dops.NEG_DOUBLE);
		MAP.put(Rops.AND_INT, Dops.AND_INT_2ADDR);
		MAP.put(Rops.AND_LONG, Dops.AND_LONG_2ADDR);
		MAP.put(Rops.OR_INT, Dops.OR_INT_2ADDR);
		MAP.put(Rops.OR_LONG, Dops.OR_LONG_2ADDR);
		MAP.put(Rops.XOR_INT, Dops.XOR_INT_2ADDR);
		MAP.put(Rops.XOR_LONG, Dops.XOR_LONG_2ADDR);
		MAP.put(Rops.SHL_INT, Dops.SHL_INT_2ADDR);
		MAP.put(Rops.SHL_LONG, Dops.SHL_LONG_2ADDR);
		MAP.put(Rops.SHR_INT, Dops.SHR_INT_2ADDR);
		MAP.put(Rops.SHR_LONG, Dops.SHR_LONG_2ADDR);
		MAP.put(Rops.USHR_INT, Dops.USHR_INT_2ADDR);
		MAP.put(Rops.USHR_LONG, Dops.USHR_LONG_2ADDR);
		MAP.put(Rops.NOT_INT, Dops.NOT_INT);
		MAP.put(Rops.NOT_LONG, Dops.NOT_LONG);

		MAP.put(Rops.ADD_CONST_INT, Dops.ADD_INT_LIT8);
		// Note: No dalvik ops for other types of add_const.

		MAP.put(Rops.SUB_CONST_INT, Dops.RSUB_INT_LIT8);
		/*
		 * Note: No dalvik ops for any type of sub_const; instead there's a
		 * *reverse* sub (constant - reg) for ints only.
		 */

		MAP.put(Rops.MUL_CONST_INT, Dops.MUL_INT_LIT8);
		// Note: No dalvik ops for other types of mul_const.

		MAP.put(Rops.DIV_CONST_INT, Dops.DIV_INT_LIT8);
		// Note: No dalvik ops for other types of div_const.

		MAP.put(Rops.REM_CONST_INT, Dops.REM_INT_LIT8);
		// Note: No dalvik ops for other types of rem_const.

		MAP.put(Rops.AND_CONST_INT, Dops.AND_INT_LIT8);
		// Note: No dalvik op for and_const_long.

		MAP.put(Rops.OR_CONST_INT, Dops.OR_INT_LIT8);
		// Note: No dalvik op for or_const_long.

		MAP.put(Rops.XOR_CONST_INT, Dops.XOR_INT_LIT8);
		// Note: No dalvik op for xor_const_long.

		MAP.put(Rops.SHL_CONST_INT, Dops.SHL_INT_LIT8);
		// Note: No dalvik op for shl_const_long.

		MAP.put(Rops.SHR_CONST_INT, Dops.SHR_INT_LIT8);
		// Note: No dalvik op for shr_const_long.

		MAP.put(Rops.USHR_CONST_INT, Dops.USHR_INT_LIT8);
		// Note: No dalvik op for shr_const_long.

		MAP.put(Rops.CMPL_LONG, Dops.CMP_LONG);
		MAP.put(Rops.CMPL_FLOAT, Dops.CMPL_FLOAT);
		MAP.put(Rops.CMPL_DOUBLE, Dops.CMPL_DOUBLE);
		MAP.put(Rops.CMPG_FLOAT, Dops.CMPG_FLOAT);
		MAP.put(Rops.CMPG_DOUBLE, Dops.CMPG_DOUBLE);
		MAP.put(Rops.CONV_L2I, Dops.LONG_TO_INT);
		MAP.put(Rops.CONV_F2I, Dops.FLOAT_TO_INT);
		MAP.put(Rops.CONV_D2I, Dops.DOUBLE_TO_INT);
		MAP.put(Rops.CONV_I2L, Dops.INT_TO_LONG);
		MAP.put(Rops.CONV_F2L, Dops.FLOAT_TO_LONG);
		MAP.put(Rops.CONV_D2L, Dops.DOUBLE_TO_LONG);
		MAP.put(Rops.CONV_I2F, Dops.INT_TO_FLOAT);
		MAP.put(Rops.CONV_L2F, Dops.LONG_TO_FLOAT);
		MAP.put(Rops.CONV_D2F, Dops.DOUBLE_TO_FLOAT);
		MAP.put(Rops.CONV_I2D, Dops.INT_TO_DOUBLE);
		MAP.put(Rops.CONV_L2D, Dops.LONG_TO_DOUBLE);
		MAP.put(Rops.CONV_F2D, Dops.FLOAT_TO_DOUBLE);
		MAP.put(Rops.TO_BYTE, Dops.INT_TO_BYTE);
		MAP.put(Rops.TO_CHAR, Dops.INT_TO_CHAR);
		MAP.put(Rops.TO_SHORT, Dops.INT_TO_SHORT);
		MAP.put(Rops.RETURN_VOID, Dops.RETURN_VOID);
		MAP.put(Rops.RETURN_INT, Dops.RETURN);
		MAP.put(Rops.RETURN_LONG, Dops.RETURN_WIDE);
		MAP.put(Rops.RETURN_FLOAT, Dops.RETURN);
		MAP.put(Rops.RETURN_DOUBLE, Dops.RETURN_WIDE);
		MAP.put(Rops.RETURN_OBJECT, Dops.RETURN_OBJECT);
		MAP.put(Rops.ARRAY_LENGTH, Dops.ARRAY_LENGTH);
		MAP.put(Rops.THROW, Dops.THROW);
		MAP.put(Rops.MONITOR_ENTER, Dops.MONITOR_ENTER);
		MAP.put(Rops.MONITOR_EXIT, Dops.MONITOR_EXIT);
		MAP.put(Rops.AGET_INT, Dops.AGET);
		MAP.put(Rops.AGET_LONG, Dops.AGET_WIDE);
		MAP.put(Rops.AGET_FLOAT, Dops.AGET);
		MAP.put(Rops.AGET_DOUBLE, Dops.AGET_WIDE);
		MAP.put(Rops.AGET_OBJECT, Dops.AGET_OBJECT);
		MAP.put(Rops.AGET_BOOLEAN, Dops.AGET_BOOLEAN);
		MAP.put(Rops.AGET_BYTE, Dops.AGET_BYTE);
		MAP.put(Rops.AGET_CHAR, Dops.AGET_CHAR);
		MAP.put(Rops.AGET_SHORT, Dops.AGET_SHORT);
		MAP.put(Rops.APUT_INT, Dops.APUT);
		MAP.put(Rops.APUT_LONG, Dops.APUT_WIDE);
		MAP.put(Rops.APUT_FLOAT, Dops.APUT);
		MAP.put(Rops.APUT_DOUBLE, Dops.APUT_WIDE);
		MAP.put(Rops.APUT_OBJECT, Dops.APUT_OBJECT);
		MAP.put(Rops.APUT_BOOLEAN, Dops.APUT_BOOLEAN);
		MAP.put(Rops.APUT_BYTE, Dops.APUT_BYTE);
		MAP.put(Rops.APUT_CHAR, Dops.APUT_CHAR);
		MAP.put(Rops.APUT_SHORT, Dops.APUT_SHORT);
		MAP.put(Rops.NEW_INSTANCE, Dops.NEW_INSTANCE);
		MAP.put(Rops.CHECK_CAST, Dops.CHECK_CAST);
		MAP.put(Rops.INSTANCE_OF, Dops.INSTANCE_OF);

		MAP.put(Rops.GET_FIELD_LONG, Dops.IGET_WIDE);
		MAP.put(Rops.GET_FIELD_FLOAT, Dops.IGET);
		MAP.put(Rops.GET_FIELD_DOUBLE, Dops.IGET_WIDE);
		MAP.put(Rops.GET_FIELD_OBJECT, Dops.IGET_OBJECT);
		/*
		 * Note: No map entries for get_field_* for non-long integral types,
		 * since they need to be handled specially (see dopFor() below).
		 */

		MAP.put(Rops.GET_STATIC_LONG, Dops.SGET_WIDE);
		MAP.put(Rops.GET_STATIC_FLOAT, Dops.SGET);
		MAP.put(Rops.GET_STATIC_DOUBLE, Dops.SGET_WIDE);
		MAP.put(Rops.GET_STATIC_OBJECT, Dops.SGET_OBJECT);
		/*
		 * Note: No map entries for get_static* for non-long integral types,
		 * since they need to be handled specially (see dopFor() below).
		 */

		MAP.put(Rops.PUT_FIELD_LONG, Dops.IPUT_WIDE);
		MAP.put(Rops.PUT_FIELD_FLOAT, Dops.IPUT);
		MAP.put(Rops.PUT_FIELD_DOUBLE, Dops.IPUT_WIDE);
		MAP.put(Rops.PUT_FIELD_OBJECT, Dops.IPUT_OBJECT);
		/*
		 * Note: No map entries for put_field_* for non-long integral types,
		 * since they need to be handled specially (see dopFor() below).
		 */

		MAP.put(Rops.PUT_STATIC_LONG, Dops.SPUT_WIDE);
		MAP.put(Rops.PUT_STATIC_FLOAT, Dops.SPUT);
		MAP.put(Rops.PUT_STATIC_DOUBLE, Dops.SPUT_WIDE);
		MAP.put(Rops.PUT_STATIC_OBJECT, Dops.SPUT_OBJECT);
		/*
		 * Note: No map entries for put_static* for non-long integral types,
		 * since they need to be handled specially (see dopFor() below).
		 */

		/*
		 * Note: No map entries for invoke*, new_array, and filled_new_array,
		 * since they need to be handled specially (see dopFor() below).
		 */
	}

	/**
	 * Returns the dalvik opcode appropriate for the given register-based
	 * instruction.
	 * 
	 * @param insn
	 *            {@code non-null;} the original instruction
	 * @return the corresponding dalvik opcode; one of the constants in
	 *         {@link Dops}
	 */
	public static Dop dopFor(Insn insn) {
		Rop rop = insn.getOpcode();

		/*
		 * First, just try looking up the rop in the MAP of easy cases.
		 */
		Dop result = MAP.get(rop);
		if (result != null) {
			return result;
		}

		/*
		 * There was no easy case for the rop, so look up the opcode, and do
		 * something special for each:
		 * 
		 * The move_exception, new_array, filled_new_array, and invoke* opcodes
		 * won't be found in MAP, since they'll each have different source
		 * and/or result register types / lists.
		 * 
		 * The get* and put* opcodes for (non-long) integral types aren't in the
		 * map, since the type signatures aren't sufficient to distinguish
		 * between the types (the salient source or result will always be just
		 * "int").
		 * 
		 * And const instruction need to distinguish between strings and
		 * classes.
		 */

		switch (rop.getOpcode()) {
		case RegOps.MOVE_EXCEPTION:
			return Dops.MOVE_EXCEPTION;
		case RegOps.INVOKE_STATIC:
			return Dops.INVOKE_STATIC;
		case RegOps.INVOKE_VIRTUAL:
			return Dops.INVOKE_VIRTUAL;
		case RegOps.INVOKE_SUPER:
			return Dops.INVOKE_SUPER;
		case RegOps.INVOKE_DIRECT:
			return Dops.INVOKE_DIRECT;
		case RegOps.INVOKE_INTERFACE:
			return Dops.INVOKE_INTERFACE;
		case RegOps.NEW_ARRAY:
			return Dops.NEW_ARRAY;
		case RegOps.FILLED_NEW_ARRAY:
			return Dops.FILLED_NEW_ARRAY;
		case RegOps.FILL_ARRAY_DATA:
			return Dops.FILL_ARRAY_DATA;
		case RegOps.MOVE_RESULT: {
			RegisterSpec resultReg = insn.getResult();

			if (resultReg == null) {
				return Dops.NOP;
			} else {
				switch (resultReg.getBasicType()) {
				case Type.BT_INT:
				case Type.BT_FLOAT:
				case Type.BT_BOOLEAN:
				case Type.BT_BYTE:
				case Type.BT_CHAR:
				case Type.BT_SHORT:
					return Dops.MOVE_RESULT;
				case Type.BT_LONG:
				case Type.BT_DOUBLE:
					return Dops.MOVE_RESULT_WIDE;
				case Type.BT_OBJECT:
					return Dops.MOVE_RESULT_OBJECT;
				default: {
					throw new RuntimeException("Unexpected basic type");
				}
				}
			}
		}

		case RegOps.GET_FIELD: {
			CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn)
					.getConstant();
			int basicType = ref.getBasicType();
			switch (basicType) {
			case Type.BT_BOOLEAN:
				return Dops.IGET_BOOLEAN;
			case Type.BT_BYTE:
				return Dops.IGET_BYTE;
			case Type.BT_CHAR:
				return Dops.IGET_CHAR;
			case Type.BT_SHORT:
				return Dops.IGET_SHORT;
			case Type.BT_INT:
				return Dops.IGET;
			}
			break;
		}
		case RegOps.PUT_FIELD: {
			CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn)
					.getConstant();
			int basicType = ref.getBasicType();
			switch (basicType) {
			case Type.BT_BOOLEAN:
				return Dops.IPUT_BOOLEAN;
			case Type.BT_BYTE:
				return Dops.IPUT_BYTE;
			case Type.BT_CHAR:
				return Dops.IPUT_CHAR;
			case Type.BT_SHORT:
				return Dops.IPUT_SHORT;
			case Type.BT_INT:
				return Dops.IPUT;
			}
			break;
		}
		case RegOps.GET_STATIC: {
			CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn)
					.getConstant();
			int basicType = ref.getBasicType();
			switch (basicType) {
			case Type.BT_BOOLEAN:
				return Dops.SGET_BOOLEAN;
			case Type.BT_BYTE:
				return Dops.SGET_BYTE;
			case Type.BT_CHAR:
				return Dops.SGET_CHAR;
			case Type.BT_SHORT:
				return Dops.SGET_SHORT;
			case Type.BT_INT:
				return Dops.SGET;
			}
			break;
		}
		case RegOps.PUT_STATIC: {
			CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn)
					.getConstant();
			int basicType = ref.getBasicType();
			switch (basicType) {
			case Type.BT_BOOLEAN:
				return Dops.SPUT_BOOLEAN;
			case Type.BT_BYTE:
				return Dops.SPUT_BYTE;
			case Type.BT_CHAR:
				return Dops.SPUT_CHAR;
			case Type.BT_SHORT:
				return Dops.SPUT_SHORT;
			case Type.BT_INT:
				return Dops.SPUT;
			}
			break;
		}
		case RegOps.CONST: {
			Constant cst = ((ThrowingCstInsn) insn).getConstant();
			if (cst instanceof CstType) {
				return Dops.CONST_CLASS;
			} else if (cst instanceof CstString) {
				return Dops.CONST_STRING;
			}
			break;
		}
		}

		throw new RuntimeException("unknown rop: " + rop);
	}
}
